import { writeFile } from "node:fs/promises";

const platforms = {
  cloudflare: {
    url: "https://platform-node-compat.pi0.workers.dev/?json",
    forceHybrid: ["console"],
    forceBuiltin: [
      "_tls_wrap",
      "assert",
      "assert/strict",
      "events",
      "net",
      "stream",
    ],
  },
  vercel: {
    url: "https://platform-node-compat.vercel.app/?json",
    // https://vercel.com/docs/functions/edge-middleware/edge-runtime#compatible-node.js-modules
    forceBuiltin: ["async_hooks", "events", "buffer", "assert", "util"],
  },
  // Deno deploy and Netlify edge are almost identical
  deno: {
    url: "https://platform-node-compat.deno.dev/?json",
    forceBuiltin: true,
  },
  netlify: {
    url: "https://platform-node-compat.netlify.app/?json",
    forceBuiltin: true,
  },
} as {
  [platform: string]: {
    url: string;
    forceHybrid?: string[];
    forceBuiltin?: true | string[];
  };
};

type NodeComptatReport = {
  _url: string;
  version: string;
  globals: {
    globalKeys: string[];
    missing: string[];
  };
  builtinModules: Record<
    string,
    | false
    | {
        exports: string[];
        missingExports: string[];
      }
  >;
};

for (const [platformName, { url, forceHybrid, forceBuiltin }] of Object.entries(
  platforms
)) {
  const report = (await fetch(url).then((res) =>
    res.json()
  )) as NodeComptatReport;

  const builtnNodeModules: [string, string[]][] = [];

  const hybridNodeCompatModules: [string, string[]][] = [];

  const notSupported: string[] = [];

  for (const [id, status] of Object.entries(report.builtinModules)) {
    if (!status) {
      if (forceHybrid?.includes(id)) {
        hybridNodeCompatModules.push([id, []]);
      } else {
        notSupported.push(id);
      }
      continue;
    }

    const missingExports = status.missingExports.filter(
      (exp) => !exp.startsWith("_")
    );

    let target =
      missingExports.length === 0 ? builtnNodeModules : hybridNodeCompatModules;
    if (forceHybrid?.includes(id)) {
      target = hybridNodeCompatModules;
    }
    if (forceBuiltin === true || forceBuiltin?.includes(id)) {
      target = builtnNodeModules;
    }

    target.push([id, missingExports]);
  }

  const code = /* js */ `// Auto generated using gen-node-compat.ts on ${new Date().toISOString().split("T")[0]}
// Source: ${url.replace(/\?json$/, "")}
// Do not edit this file manually

// prettier-ignore
export const builtnNodeModules = [
${builtnNodeModules
  .sort()
  .map(([id, missing]) =>
    missing.length > 0
      ? `  "${id}", // Missing exports: ${missing.join(", ")}`
      : `  "${id}",`
  )
  .join("\n")}
];

// prettier-ignore
export const hybridNodeModules = [
${hybridNodeCompatModules
  .sort((a, b) => a[0].localeCompare(b[0]))
  .map(([id, missing]) =>
    missing.length > 0
      ? `  "${id}", // Missing exports: ${missing.join(", ")}`
      : `  "${id}",`
  )
  .join("\n")}
];

// prettier-ignore
export const unsupportedNodeModules = [
${notSupported.map((id) => `  "${id}",`).join("\n")}
];
`;

  await writeFile(
    new URL(
      `../src/presets/_unenv/node-compat/${platformName}.ts`,
      import.meta.url
    ),
    code
  );
}

export {};
